package puntaelena.facilitylocation.hillclimbing.solver.agents;

import jfuzzyprincepless.negotiation.INegotiationContext;
import laboratoryQ.computation.BaseUtils;
import puntaelena.facilitylocation.hillclimbing.solver.services.IFcltLocDataService;
import puntaelena.graphsupport.FacilitiesGraph;
import puntaelena.graphsupport.GraphSupportFront;
import static puntaelena.facilitylocation.hillclimbing.solver.agents.FcltCityNegotiateComputator.*;

public class FacilityAgainstFacilityAgent
	extends BaseFacilityAgent
{
	public FacilityAgainstFacilityAgent(int facility) {
		super(facility);
	}

	@Override
	public boolean negotiate(INegotiationContext context) {
		IFcltLocDataService flServ =
			context.getService(IFcltLocDataService.class);
		FacilitiesGraph fgraph = flServ.getFgraph();
		int facilitiesCount = fgraph.getFacilitiesCount();
		boolean[] isFcltOpen = flServ.getFcltOpen();
		if (isFcltOpen[facility]) {
			return false;
		}

		for (int enemyFclt = 0; enemyFclt < facilitiesCount; enemyFclt++) {
			if (isFcltOpen[enemyFclt]) {
				boolean successed = againstFacility(
					context,
					enemyFclt
				);
				if (successed) {
					return true;
				}
			}
		}
		
		return false;
	}

	private boolean againstFacility(
		INegotiationContext context,
		int enemyFclt
	) {
		IFcltLocDataService flServ =
			context.getService(IFcltLocDataService.class);
		FacilitiesGraph fgraph = flServ.getFgraph();
		int[] witnesses = flServ.getWitnesses();
		boolean[] isFcltOpen = flServ.getFcltOpen();
		double[] facilitiesPrices = fgraph.getFacilitiesPrices();

		double pre_cost = BaseUtils.globalAssertionEnabled() ?
			GraphSupportFront.computeFacilityLocationCost(
				fgraph,
				isFcltOpen,
				witnesses
			) :
			Double.NaN;

		double redistributeProfit = Double.NaN;
		double exchangeProfit = Double.NaN;
		isFcltOpen[facility] = true;
		try {
			redistributeProfit = computeRedistributeFacilityProfit(
				fgraph, witnesses, isFcltOpen, enemyFclt
			);
			exchangeProfit = redistributeProfit +
				facilitiesPrices[enemyFclt] -
				facilitiesPrices[facility];
		} finally {
			isFcltOpen[facility] = false;
		}

		boolean success = exchangeProfit > eps;
		if (success) {
			isFcltOpen[facility] = true;
			isFcltOpen[enemyFclt] = false;
		}

		double checkProfit = computeBestAllWitnessProfit(
			fgraph,
			witnesses,
			isFcltOpen,
			true
		);

		assert (checkProfit > -eps || success);
		assert redistributeProfit <= checkProfit + eps || !success;
		double post_cost = BaseUtils.globalAssertionEnabled() ?
			GraphSupportFront.computeFacilityLocationCost(
				fgraph,
				isFcltOpen,
				witnesses
			) :
			Double.NaN;
		assert post_cost <= pre_cost + eps;
		return success;
	}

	public int getFacility() {
		return facility;
	}
}